package com.yjsn.base.interceptor;


import com.alibaba.fastjson2.JSON;
import com.yjsn.base.config.RedisCache;
import com.yjsn.base.config.RequestWrapper;
import com.yjsn.base.exception.SignException;
import com.yjsn.base.utils.SignUtil;
import com.yjsn.base.utils.StringUtils;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author: Liu Yang
 * @create: 2024-04-03 15:04
 * @Description:
 */
@Component
public class SignInterceptor implements HandlerInterceptor {

    private static Logger log = LoggerFactory.getLogger(SignInterceptor.class);

    @Value("#{'${aigc.sign.exclude.Url: }'.split(',')}")
    private List<String> excludeUrls;

    @Value("#{'${search.sign.external.Url: }'.split(',')}")
    private List<String> externalUrls;

    @Value("${sign.enable:true}")
    private boolean enable;

    @Value("${reqeust.expire.time:60}")
    private int expireTime;

    @Value("${aes.key}")
    private String AES_KEY;

    @Value("${external.sign.key}")
    private String externalSignKey;

    @Resource
    RedisCache redisCache;
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        String url = httpServletRequest.getServletPath();
        if(!enable) return true;
        if (!StringUtils.matches(url, excludeUrls)) {
            RequestWrapper requestWrapper = new RequestWrapper(httpServletRequest);
            String body = requestWrapper.getBodyString();
            log.info("current request body info is {}", body);
            // 获取请求头参数
            String source = getSource(httpServletRequest);
            String requestId = getRequestId(httpServletRequest);
            Long timestamp = getDateTimestamp(httpServletRequest);
            String random = getRandom(httpServletRequest);
            log.info("current source is:{}, Sign is : [{}],timestamp is : [{}]",source,requestId,timestamp);
            if (!checkRequestId(body,requestId,timestamp,random,source,url)) {// 签名未 校验通过
                throw new SignException("签名错误");
            }
            httpServletRequest.setAttribute("requestWrapper",requestWrapper);
        }
        return true;
    }



    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }

    private Long getDateTimestamp( HttpServletRequest request) {
        String timestampStr= request.getHeader("timestamp");
        if (StringUtils.isBlank(timestampStr)) {
            log.info("######timestamp is null");
            throw new SignException(" timestamp is null");
        }
        long timestamp = Long.parseLong(timestampStr);
        long currentTimeMillis = System.currentTimeMillis();
        //有效时长为1分钟
        if (currentTimeMillis - timestamp > 1000 * 60 ) {
            log.info("#####timestamp:{},currentTime:{}",timestamp,currentTimeMillis);
            throw new SignException("timestamp");
        }
        return timestamp;
    }

    private String getRandom(HttpServletRequest request) {
        String random= request.getHeader("random");
        if (StringUtils.isBlank(random)) {
            log.info("######random is null");
            throw new SignException("random is nul");
        }
        return random;
    }

    private String getSource(HttpServletRequest request) {
        String source= request.getHeader("source");
        if (StringUtils.isBlank(source)) {
            log.info("######source is null");
            throw new SignException("source is nul");
        }
        return source;
    }

    private String getRequestId(HttpServletRequest request) {
        String sign = request.getHeader("sign");
        if (StringUtils.isBlank(sign)) {
            throw new SignException("sign is null");
        }
        //如果requestId存在redis中直接返回
        String temp = redisCache.getCacheObject(sign);
        if (StringUtils.isNotBlank(temp)) {
            log.info("sign is validate: {}",temp);
            throw new SignException("sign 失效！");
        }
        redisCache.setCacheObject(sign ,sign,expireTime, TimeUnit.SECONDS);
        return sign;
    }

    public boolean checkRequestId(String bodyStr,String requestId,Long timestamp,String random,String source,String url){
        Map<String, Object> paramsMap = JSON.parseObject(bodyStr,Map.class);
        if(Objects.isNull(paramsMap)){
            paramsMap = new HashMap<>();
        }
        paramsMap.put("timestamp",timestamp);
        paramsMap.put("random",random);
        paramsMap.put("source",source);
        String checkSign;
        if (StringUtils.matches(url, externalUrls)){
            checkSign = SignUtil.generateSign(paramsMap, externalSignKey);
        }else{
            checkSign = SignUtil.generateSign(paramsMap, AES_KEY);
        }
        if(checkSign.equals(requestId)) return true;
        return false;
    }
}
